library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.typeDefinitions.all;

entity cpuPipeline is
  
  port (
    clk    : in std_logic;
    nReset : in std_logic;


    dMemDataR : in  std_logic_vector(31 downto 0);
    dMemWait  : in  std_logic;
    dMemRen   : out std_logic;          --!!!!!!!!!!!!!!!!!!!!!!!
    dMemDataW : out std_logic_vector(31 downto 0);
    dMemAddr  : out std_logic_vector(31 downto 0);
    dMemWen   : out std_logic;

    iMemWait : in  std_logic;
    iMemAddr : out std_logic_vector(31 downto 0);  --need more signals?
    iMemData : in  std_logic_vector(31 downto 0);
    iMemRen  : out std_logic;

    halt : out std_logic;

    instruction : out interStageOperation;

    forwardIn  : in  forwardSignalsIn;
    forwardOut : out forwardSignalsOut;

    hold       : in stageHoldSignals;
    invalidate : in stageInvalidateSignals
    );

end cpuPipeline;

architecture cpuPipeline_arch of cpuPipeline is

  component fetchStage
    port (
      clk        : in std_logic;
      nReset     : in std_logic;
      holdStage  : in std_logic;
      invalidate : in std_logic;

      sel      : in pcSel;
      busA     : in std_logic_vector(31 downto 0);
      jumpAddr : in std_logic_vector(25 downto 0);

      iMemAddr : out std_logic_vector(31 downto 0);  --need more signals?
      iMemData : in  std_logic_vector(31 downto 0);
      pcOld_in : in  std_logic_vector(31 downto 0);
      pcOld    : out std_logic_vector(31 downto 0);
      halt     : in  std_logic;
      holdPC   : in  std_logic;

      instruction : out std_logic_vector(31 downto 0);
      nPC         : out std_logic_vector(31 downto 0));

  end component;

  component idStage
    port (
      clk            : in std_logic;
      nReset         : in std_logic;
      holdStage      : in std_logic;
      invalidate     : in std_logic;
      nPC_in         : in std_logic_vector(31 downto 0);
      pcOld_in       : in std_logic_vector(31 downto 0);
      instruction_in : in std_logic_vector(31 downto 0);

      writeDestination : in std_logic_vector(4 downto 0);
      writeData        : in std_logic_vector(31 downto 0);
      Wen              : in std_logic;

      forwardIn  : in  idForwardIn;
      forwardOut : out idForwardOut;

      halt               : out std_logic;
      busA               : out std_logic_vector(31 downto 0);
      busB               : out std_logic_vector(31 downto 0);
      rs                 : out std_logic_vector(4 downto 0);
      rt                 : out std_logic_vector(4 downto 0);
      rdat1_out          : out std_logic_vector(31 downto 0);
      immediate          : out std_logic_vector(31 downto 0);
      upper              : out std_logic_vector(31 downto 0);
      pcOld              : out std_logic_vector(31 downto 0);
      jumpAddr           : out std_logic_vector(25 downto 0);
      destination        : out std_logic_vector(4 downto 0);
      shftamnt           : out std_logic_vector(4 downto 0);
      nPC_Sel            : out pcSel;
      instruction        : out operation;
      instruction_decode : out operation;
      nPC                : out std_logic_vector(31 downto 0));
  end component;

  component execStage
    port (
      clk            : in  std_logic;
      nReset         : in  std_logic;
      holdStage      : in  std_logic;
      invalidate     : in  std_logic;
      nPC_in         : in  std_logic_vector(31 downto 0);
      instruction_in : in  operation;
      rs_in          : in  std_logic_vector(4 downto 0);
      rt_in          : in  std_logic_vector(4 downto 0);
      busA_in        : in  std_logic_vector(31 downto 0);
      busB_in        : in  std_logic_vector(31 downto 0);
      immediate_in   : in  std_logic_vector(31 downto 0);
      upper_in       : in  std_logic_vector(31 downto 0);
      shftamnt_in    : in  std_logic_vector(4 downto 0);
      destination_in : in  std_logic_vector(4 downto 0);
      destination    : out std_logic_vector(4 downto 0);
      rt             : out std_logic_vector(4 downto 0);
      aluResult      : out std_logic_vector(31 downto 0);
      busB           : out std_logic_vector(31 downto 0);
      upper          : out std_logic_vector(31 downto 0);
      instruction    : out operation;

      forwardIn  : in  execForwardIn;
      forwardOut : out execForwardOut;

      nPC : out std_logic_vector(31 downto 0));
  end component;

  component memStage
    port (
      clk        : in std_logic;
      nReset     : in std_logic;
      holdStage  : in std_logic;
      invalidate : in std_logic;

      nPC_in         : in std_logic_vector(31 downto 0);
      upper_in       : in std_logic_vector(31 downto 0);
      aluResult_in   : in std_logic_vector(31 downto 0);
      busB_in        : in std_logic_vector(31 downto 0);
      destination_in : in std_logic_vector(4 downto 0);
      rt_in          : in std_logic_vector(4 downto 0);
      instruction_in : in operation;

      memWait   : in  std_logic;
      dMemDataR : in  std_logic_vector(31 downto 0);
      dMemDataW : out std_logic_vector(31 downto 0);
      dMemAddr  : out std_logic_vector(31 downto 0);
      dMemWen   : out std_logic;
      dMemRen   : out std_logic;


      upper       : out std_logic_vector(31 downto 0);
      aluResult   : out std_logic_vector(31 downto 0);
      dataMem     : out std_logic_vector(31 downto 0);
      destination : out std_logic_vector(4 downto 0);
      instruction : out operation;


      forwardIn  : in  memForwardIn;
      forwardOut : out memForwardOut;

      nPC : out std_logic_vector(31 downto 0));

  end component;

  component  wbInterstageReg
  port (
    clk            : in std_logic;
    nReset         : in std_logic;
    holdStage      : in std_logic;
    invalidate     : in std_logic;
	
  
    dataMem_in        : in std_logic_vector(31 downto 0);
    destination_in : in std_logic_vector(4 downto 0);
	
 
    dataMem        	: out std_logic_vector(31 downto 0);
    destination 	: out std_logic_vector(4 downto 0));   
end component;

  signal t_instruction        : interStageOperation;
  signal nPC                  : interStage32;
  signal destination          : interStage5;
  signal fetch                : fetchUnique;
  signal id                   : idUnique;
  signal exec                 : execUnique;
  signal mem                  : memUnique;
  signal t_dmemWen, t_dmemRen : std_logic;
  signal forward              : forwardSignals;

begin  -- cpuPipeline_arch

  forward.ins <= forwardIn;
  forwardOut  <= forward.outs;

  forward.outs.wb.destination <= destination.mem;
  forward.outs.wb.writeData   <= id.wData;

  dMemRen     <= t_dMemRen;
  dMemWen     <= t_dMemWen;
 -- iMemRen     <= (not t_dMemWen) and (not t_dMemRen) and nReset;
  iMemRen <= (not dMemWait) and nReset;
  instruction <= t_instruction;
  fetch.busA  <= id.rdat1;
  id.wDest    <= destination.mem;
  
  fetch_c : fetchStage port map (
    clk         => clk,
    nReset      => nReset,
    holdStage   => hold.fetch,
    invalidate  => invalidate.fetch,
    sel         => fetch.sel,
    busA        => fetch.busA,
    jumpAddr    => fetch.jumpAddr,
    iMemAddr    => iMemAddr,
    iMemData    => iMemData,
    pcOld_in    => fetch.pcOld_in,
    pcOld       => fetch.pcOld,
    halt        => fetch.halt,
    holdPC      => hold.pc,
    instruction => t_instruction.fetch,
    nPC         => nPC.fetch);


  id_c : idStage port map (
    clk                => clk,
    nReset             => nReset,
    holdStage          => hold.id,
    invalidate         => invalidate.id,
    nPC_in             => nPC.fetch,
    pcOld_in           => fetch.pcOld,
    instruction_in     => t_instruction.fetch,
    instruction_decode => t_instruction.decode,

    writeDestination => id.wDest,
    writeData        => id.wData,
    Wen              => id.wEn,


    forwardIn  => forward.ins.id,
    forwardOut => forward.outs.id,

    rs => id.rs,
    rt => id.rt,

    halt        => fetch.halt,
    busA        => id.busA,
    busB        => id.busB,
    rdat1_out   => id.rdat1,
    immediate   => id.immediate,
    upper       => id.upper,
    pcOld       => fetch.pcOld_in,
    jumpAddr    => fetch.jumpAddr,
    destination => destination.id,
    shftamnt    => id.shftamnt,
    nPC_Sel     => fetch.sel,


    instruction => t_instruction.id,  -- t_instruction.id needs to go to t_forward.id.instruction
    nPC         => nPC.id);


  exec_c : execStage port map (
    clk            => clk,
    nReset         => nReset,
    holdStage      => hold.exec,
    invalidate     => invalidate.exec,
    nPC_in         => nPC.id,
    nPC            => nPC.exec,
    instruction_in => t_instruction.id,
    instruction    => t_instruction.exec,

    rs_in => id.rs,
    rt_in => id.rt,

    forwardIn  => forward.ins.exec,
    forwardOut => forward.outs.exec,

    busA_in        => id.busA,
    busB_in        => id.busB,
    immediate_in   => id.immediate,
    upper_in       => id.upper,
    shftamnt_in    => id.shftamnt,
    destination_in => destination.id,
    rt             => mem.rt,
    destination    => destination.exec,
    aluResult      => exec.aluResult,
    busB           => exec.busB,
    upper          => exec.upper);

  mem_c : memStage port map (
    clk            => clk,
    nReset         => nReset,
    holdStage      => hold.mem,
    invalidate     => invalidate.mem,
    nPC_in         => nPC.exec,
    nPC            => nPC.mem,
    upper_in       => exec.upper,
    upper          => mem.upper,
    aluResult      => mem.aluResult,
    aluResult_in   => exec.aluResult,
    busB_in        => exec.busB,
    destination_in => destination.exec,
    destination    => destination.mem,
    instruction_in => t_instruction.exec,
    instruction    => t_instruction.mem,
    rt_in          => mem.rt,

    forwardIn  => forward.ins.mem,
    forwardOut => forward.outs.mem,

    dataMem   => mem.dataMem,
    memWait   => dMemWait,
    dMemDataR => dMemDataR,
    dMemDataW => dMemDataW,
    dMemAddr  => dMemAddr,
    dMemWen   => t_dMemWen,
    dMemRen   => t_dMemRen              --!!!!!!!!!!!!!!
    );

 wb_c : wbInterstageReg port map (
   clk            => clk,
   nReset         => nReset,
   holdStage      => hold.wb,
   invalidate     => invalidate.wb,
   dataMem_in     => forward.outs.wb.writeData,
   destination_in => forward.outs.wb.destination,
   dataMem        => forward.outs.wb2.writeData,
   destination    => forward.outs.wb2.destination);

  regWrite : process (t_instruction.mem)
  begin  -- process regW
    case t_instruction.mem is
      when o_jr | o_beq | o_bne | o_sw | o_ll | o_sc | o_halt | o_nop =>
        id.wEn <= '0';
      when others =>
        id.wEn <= '1';
    end case;
  end process regWrite;

  toReg : process (t_instruction.mem, mem, nPC)
  begin  -- process memToReg 0=ALU 1=DataMem 2=shifted imm 3=PC+4?
    case t_instruction.mem is
      when o_ll | o_lw =>
        id.wData <= mem.dataMem;
      when others =>                    --rt
        id.wData <= mem.aluResult;
    end case;
  end process toReg;

  halt <= '1' when (t_instruction.mem = o_halt) else
          '0';
  
end cpuPipeline_arch;
